package net.homelinux.chaoswg.io.renga.core.gui.operations;

import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JOptionPane;

import net.homelinux.chaoswg.io.datatypes.Unit;
import net.homelinux.chaoswg.io.renga.core.gui.DrawingArea;
import net.homelinux.chaoswg.io.renga.core.gui.GUI;
import net.homelinux.chaoswg.io.renga.core.gui.operations.helper.Connection;
import net.homelinux.chaoswg.io.renga.core.gui.operations.visualisations.NamedBlockVisualisation;

import org.jdom.Element;

public class CompositeOperation extends Operation implements Observer {
    protected Control control = null;
    protected String name = null;

    public CompositeOperation () {
        this.visualisation = new NamedBlockVisualisation (this.name);
        this.initConnections(0);
    }
    
    public CompositeOperation (final Control toViewAndControl) {
        this.control = toViewAndControl;
        this.name = "";
        toViewAndControl.addObserver(this);
        this.visualisation = new NamedBlockVisualisation (this.name);
        this.initConnections(0);
    }

    public CompositeOperation (final Control toViewAndControl, final String name) {
        this.control = toViewAndControl;
        this.name = name;
        toViewAndControl.addObserver(this);
        this.visualisation = new NamedBlockVisualisation (this.name);
        this.initConnections(0);
    }

    @Override
    public Object apply(final int slot) {
        this.visualisation.setHighlighted(true);
        setChanged();
        notifyObservers(true);

        if (slot >= outputs) {
            return null;
        }

        final List<Operation> ends = control.getOutputs();

        if (ends.size() == 0) {
            return new Unit ();
        }

        for (final Operation op : ends) {
            if (op instanceof FunctionOutputOperation) {
                FunctionOutputOperation foo = ((FunctionOutputOperation) op);
                if (foo.getIsOutputNumber() == slot) {
                    
                    this.visualisation.setHighlighted(false);
                    setChanged();
                    notifyObservers(false);
                    
                    return foo.apply(0);
                }
                this.visualisation.setHighlighted(false);
                setChanged();
            }
        }
        
        this.visualisation.setHighlighted(false);
        setChanged();
        notifyObservers(false);

        return null;
    }

    public Control getControl() {
        return control;
    }

    public String toString () {
        return getName() != null  ? getName() : "Composite operation";
    }
    @Override
    public void reset () {
        super.reset();
        for (Operation op : control.getOperations()) {
            op.reset();
        }
    }

    public void update (final Observable observable, final Object obj) {
        if (observable == this.control) {
            if (inputs != control.getInputs().size() || outputs != control.getOutputs().size()) {
                this.inputs = control.getInputs().size();
                this.outputs = control.getOutputs().size();

                connections = new Connection[inputs];

                for (int i = 0, n = connections.length; i < n; ++i) {
                    connections[i] = new Connection();
                }

                visualisation.setInputs(this.inputs);
                visualisation.setOutputs(this.outputs);
            }
        }
    }

    public void setName (final String name) {
        this.name = name;
        ((NamedBlockVisualisation)visualisation).setName(name);
    }

    public String getName () {
        return this.name;
    }

    @Override
    public String getDoubleClickActionName() {
        return "Open to edit";
    }

    @Override
    public void doubleClick(final DrawingArea dv) {
        String name = this.getName();
        
        name = JOptionPane.showInputDialog(dv, "Please input a name for the function", this.getName() == null ? "" : this.getName());
        
        if (name != null) {
            this.setName(name);
            this.getControl().addInputOutputListener(dv);
            new GUI (this);
        }
    }
    
    @Override
    public boolean fullyConnected () {
        if (!super.fullyConnected()) {
            return false;
        }
        return control.fullyConnected();        
    }
    
    @Override
    public Element writeYourself () {
        final Element toReturn = super.writeYourself();
        
        if (name != null) {
            Element nameElement  = new Element ("name");
            nameElement.setText(name);
            
            toReturn.addContent(nameElement);
        }
        
        Element inputsElement = new Element ("inputs");
        inputsElement.setText("" + inputs);
        
        Element outputsElemnt = new Element ("outputs");
        outputsElemnt.setText("" + outputs);
        
        toReturn.addContent(inputsElement);
        toReturn.addContent(outputsElemnt);
        
        for (final Operation op : control.getOperations()) {
            toReturn.addContent(op.writeYourself());
        }
        
        return toReturn;
    }

    public void setControl(final Control control) {
        this.control = control;
    }
    
    public void setInputs (final int inputs) {
        this.inputs = inputs;
        this.visualisation.setInputs(inputs);
    }
    
    public void setOutputs (final int outputs) {
        this.outputs = outputs;
        this.visualisation.setOutputs(outputs);
    }
}
